﻿using System;
using System.Collections.Generic;

namespace Hsenl {
    public enum LoadSceneMode {
        Single,
        Additional,
    }

    public enum SceneType {
        Normal,
        DontDestroyOnLoad,
    }

    public static partial class SceneManager {
        internal static readonly Dictionary<string, Scene> scenes = new();
        private static Scene _dontDestroyScene;
        private static Scene _activeScene;

        public static Scene ActiveScene => _activeScene;

        public static Scene DontDestroyScene {
            get {
                if (_dontDestroyScene != null) 
                    return _dontDestroyScene;
                
                var scene = LoadScene("DontDestroyOnLoad", LoadSceneMode.Additional);
                _dontDestroyScene = scene;
                scene.sceneType = SceneType.DontDestroyOnLoad;
                return scene;
            }
        }

        public static event Action<Scene> OnSceneLoaded;

        public static void UnloadAllScene() {
            if (ActiveScene != null) {
                if (!ActiveScene.IsDisposing)
                    Object.Destroy(ActiveScene);

                _activeScene = null;
            }

            if (DontDestroyScene != null) {
                if (!DontDestroyScene.IsDisposing)
                    Object.Destroy(DontDestroyScene);

                _dontDestroyScene = null;
            }

            foreach (var kv in scenes) {
                Object.Destroy(kv.Value);
            }

            scenes.Clear();
        }

        public static Scene LoadScene(string name, LoadSceneMode mode) {
            Scene scene;
            switch (mode) {
                case LoadSceneMode.Single:
                    if (ActiveScene != null) Object.Destroy(ActiveScene);
                    scene = SceneFactory.Create(name);
                    _activeScene = scene;
                    break;
                case LoadSceneMode.Additional:
                    scene = SceneFactory.Create(name);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(mode), mode, null);
            }

            scene.sceneType = SceneType.Normal;
            scenes.Add(name, scene);
            try {
                OnSceneLoaded?.Invoke(scene);
            }
            catch (Exception e) {
                Log.Error(e);
            }

            return scene;
        }

        public static Scene GetScene(string name) {
            scenes.TryGetValue(name, out var scene);
            return scene;
        }

        internal static void RemoveScene(string name) {
            scenes.TryGetValue(name, out var scene);
            if (ActiveScene == scene) _activeScene = null;
            scenes.Remove(name);
        }

        public static void MoveEntityToScene(Entity entity, Scene scene) {
            Assert.NullReference(entity, $"scene is null '{scene.sceneName}'");
            if (entity.Scene == scene)
                return;

            if (entity.Parent != null)
                throw new Exception("Entity is not root entity, can't move scene");
            
            entity.SetSceneInternal(scene, null, null);
        }
    }
}